package com.edfu.commons.email.dao.impl;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import com.edfu.commons.email.vo.TemplateContent;
import com.edfu.commons.email.vo.Template;
import com.edfu.commons.email.dao.IEmailTemplateDAO;
import com.edfu.commons.email.entities.EmailEventEntity;
import com.edfu.commons.email.entities.EmailTemplateContentEntity;
import com.edfu.commons.email.entities.EmailTemplateEntity;
import com.edfu.commons.email.exceptions.EventNotFoundException;
import com.edfu.commons.email.exceptions.TemplateNotFoundException;
import com.edfu.commons.email.util.BeanTransformer;

/**
 * @author naresred
 *
 */
@Component
public class EmailTemplateDAOImpl implements IEmailTemplateDAO {
	
	private EntityManager entityManager;

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	private static Logger logger = Logger.getLogger(EmailTemplateDAOImpl.class);
	
	/**
	 * Creates a new email template with specified information. If the event information is not found, this method throws
	 * @EventNotFoundException
	 * 
	 * @param templateName
	 * @param localeName
	 * @param content
	 * @param eventName
	 * @throws EventNotFoundException
	 */
	@Override
	public void createTemplate(String templateName, String description, String localeName, String content, String eventName, boolean isDefault, boolean isActive) throws EventNotFoundException {
		logger.debug("Entered [createTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if event already exists or not
		EmailEventEntity ev = getEventEntity(eventName);
		
		if (ev == null) {
			throw new EventNotFoundException("Event not found in the system");
		}
		
		// it is assumed that there will be only one default template for an event. So throw Runtime exception if default template
		// for the event already exists. Before creating the new default template it is mandatory to mark the existing default template
		// as non default
		
		if (isDefault && isDefaultTemplateExists(ev.getId(), templateName)) {
			throw new RuntimeException("Default template for the event " + ev.getName() + " already exists.");
		}
		
		EmailTemplateEntity et = new EmailTemplateEntity();
		et.setName(templateName);
		et.setDescription(description);
		et.setLocale(localeName);
		et.setContent(content);		
		
		et.setDefaultTemplate(isDefault ? 1 : 0);
		
		// there can be multiple active templates with same template name for a single event. So no need to check for active templates in the system
		et.setActive(isActive ? 1 : 0); 
		
		et.setEmailEvent(ev);
		
		entityManager.persist(et);
	}
	
	@SuppressWarnings("unchecked")
	private boolean isDefaultTemplateExists(int eventId, String templateName) {
		Query query = entityManager.createQuery("select e from EmailTemplateEntity e where e.defaultTemplate = :defaultTemplate and e.emailEvent.id = :eventId and e.name = :templateName");
		query.setParameter("defaultTemplate", 1);
		query.setParameter("eventId", eventId);
		query.setParameter("templateName", templateName);
		List<EmailTemplateEntity> templates = query.getResultList();
		
		if (templates != null && templates.size() > 0) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Creates a new email template with specified information. If the event information is not found, this method throws
	 * @EventNotFoundException
	 * 
	 * @param event
	 * @throws EventNotFoundException
	 */
	@Override
	public void createTemplate(Template template) throws EventNotFoundException {
		createTemplate(template.getName(), template.getDescription(), template.getLocale(), template.getContent(), template.getEvent().getEventName(), template.isDefaultTemplate(), template.isActive());
	}
	
	@SuppressWarnings("unchecked")
	private EmailEventEntity getEventEntity(String eventName) {
		Query query = entityManager.createQuery("select e from EmailEventEntity e where e.name = :eventName");
		query.setParameter("eventName", eventName);
		
		List<EmailEventEntity> entityList = query.getResultList();
		EmailEventEntity ev = null;
		
		if (entityList != null && entityList.size() > 0) {
			ev = entityList.get(0);
		}
		
		return ev;	
	}
	
	/**
	 * Updates an email template with the information provided. As there can be multiple templates with the same name 
	 * (template name is NOT unique) template id must be provided. If the template id is not found in the system this
	 * method throws @TemplateNotFoundException  
	 * 
	 * @param templateId
	 * @param templateName
	 * @param description
	 * @param localeName
	 * @param content
	 * @throws TemplateNotFoundException
	 * @throws EventNotFoundException
	 */
	@Override
	public void updateTemplate(int templateId, String templateName, String description, String localeName, String content, String eventName) throws TemplateNotFoundException, EventNotFoundException {
		logger.debug("Entered [updateTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		if (!eventName.equals(et.getEmailEvent().getName())) {
			et.setEmailEvent(getEventEntity(eventName));
		}
		
		et.setName(templateName);
		et.setDescription(description);
		et.setLocale(localeName);
		et.setContent(content);
		
		entityManager.merge(et);
	}
	
	/**
	 * Updates an email template with the information provided. As there can be multiple templates with the same name 
	 * (template name is NOT unique) template id must be provided. If the template id is not found in the system this
	 * method throws @TemplateNotFoundException
	 * 
	 * @param template
	 * @throws TemplateNotFoundException
	 * @throws EventNotFoundException 
	 */
	public void updateTemplate(Template template) throws TemplateNotFoundException, EventNotFoundException {
		updateTemplate(template.getId(), template.getName(), template.getDescription(), template.getLocale(), template.getContent(), template.getEvent().getEventName());
	}
	
	/**
	 * Deactivates an email template. Deactivated templates can't be used for sending emails without being activated.
	 * 
	 * @param templateId
	 * @throws TemplateNotFoundException
	 */
	public void deactivateTemplate(int templateId) throws TemplateNotFoundException {
		logger.debug("Entered [deactivateTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		et.setActive(0); //deactivates an email template
		entityManager.merge(et);
	}
	
	/**
	 * Marks an email template as non default.
	 * 
	 * @param templateId
	 * @throws TemplateNotFoundException
	 */
	@Override
	public void markTemplateAsNonDefault(int templateId) throws TemplateNotFoundException {
		logger.debug("Entered [deactivateTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		et.setDefaultTemplate(0); // Marks template as non default
		entityManager.merge(et);
	}
	
	/**
	 * Marks an email template as default.
	 * 
	 * @param templateId
	 * @throws TemplateNotFoundException
	 */
	@Override
	public void markTemplateAsDefault(int templateId) throws TemplateNotFoundException {
		logger.debug("Entered [deactivateTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		et.setDefaultTemplate(1); // Marks template as default
		entityManager.merge(et);
	}
	
	/**
	 * Activates a template
	 * 
	 * @param templateId
	 * @throws TemplateNotFoundException
	 */
	public void activateTemplate(int templateId) throws TemplateNotFoundException {
		logger.debug("Entered [activateTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		et.setActive(1); //Activates an email template
		entityManager.merge(et);
	}
	
	/**
	 * Deactivates all templates associated to an event
	 * 
	 * @param eventName
	 * @throws EventNotFoundException
	 * @throws TemplateNotFoundException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void deactivateTemplates(String eventName) throws EventNotFoundException, TemplateNotFoundException {
		logger.debug("Entered [deactivateTemplates] method of [EmailTemplateDAOImpl]");
		
		// Check if event already exists or not
		EmailEventEntity ev = getEventEntity(eventName);
		
		if (ev == null) {
			throw new EventNotFoundException("Event not found in the system");
		}
		
		Query query = entityManager.createQuery("select e from EmailTemplateEntity e where e.emailEvent.id = :eventId and e.active = :active");
		query.setParameter("eventId", ev.getId());
		query.setParameter("active", 1);  // search only for activate templates
		
		List<EmailTemplateEntity> templateEntities = query.getResultList();
		
		for (EmailTemplateEntity templateEntity : templateEntities) {
			deactivateTemplate(templateEntity.getId());
		}		
	}
	
	/**
	 * Activates all templates associated to an event
	 * 
	 * @param eventName
	 * @throws EventNotFoundException
	 * @throws TemplateNotFoundException 
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void activateTemplates(String eventName) throws EventNotFoundException, TemplateNotFoundException {
		logger.debug("Entered [activateTemplates] method of [EmailTemplateDAOImpl]");
		
		// Check if event already exists or not
		EmailEventEntity ev = getEventEntity(eventName);
		
		if (ev == null) {
			throw new EventNotFoundException("Event not found in the system");
		}
		
		Query query = entityManager.createQuery("select e from EmailTemplateEntity e where e.emailEvent.id = :eventId and e.active = :active");
		query.setParameter("eventId", ev.getId());
		query.setParameter("active", 0);  // search only for deactivated templates
		
		List<EmailTemplateEntity> templateEntities = query.getResultList();
		
		for (EmailTemplateEntity templateEntity : templateEntities) {
			activateTemplate(templateEntity.getId());
		}
	}
	
	/**
	 * Retrieves a template with specified Id. If not found, throws @TemplateNotFoundException
	 * 
	 * @param templateId
	 * @return Template
	 * @throws TemplateNotFoundException
	 */
	@Override
	public Template getTemplate(int templateId) throws TemplateNotFoundException {
		logger.debug("Entered [getTemplate] method of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		return populateTemplateImages(BeanTransformer.transform(et));
	}
	
	@SuppressWarnings("unchecked")
	private Template populateTemplateImages(Template template) {
		// search for template images as well
		Query query = entityManager.createQuery("select e from EmailTemplateContentEntity e where e.emailTemplate.id = :templateId");
		query.setParameter("templateId", template.getId());
		
		List<EmailTemplateContentEntity> imageEntityList = query.getResultList();
		if (imageEntityList != null && imageEntityList.size() > 0) {			
			List<TemplateContent> images = new ArrayList<TemplateContent>();
			List<TemplateContent> attachments = new ArrayList<TemplateContent>();
			TemplateContent templateContent = null;
			for (EmailTemplateContentEntity eti : imageEntityList) {
				templateContent = new TemplateContent();
				templateContent.setId(eti.getId());
				templateContent.setName(eti.getName());
				templateContent.setMimeType(eti.getMimeType());
				templateContent.setContent(new ByteArrayInputStream(eti.getContent()));
				
				if (eti.getAttachment() == 1) {
					attachments.add(templateContent);
				} else {
					images.add(templateContent);
				}				
			}
			
			template.setImages(images);
			template.setAttachments(attachments);
		}
		
		return template;
	}
	
	/**
	 * Retrieves all supported locales for an event. Checks only for active templates.
	 * 
	 * @param eventName
	 * @return Set<String>
	 * @throws TemplateNotFoundException
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Set<String> getSupportedLocales(String eventName) throws EventNotFoundException {
		logger.debug("Entered [getActiveTemplates] method of [EmailTemplateDAOImpl]");
		
		Query query = entityManager.createQuery("select e from EmailTemplateEntity e where e.active = :active and e.emailEvent.name = :eventName");
		query.setParameter("active", 1);  // search only for active templates
		query.setParameter("eventName", eventName);
		
		List<EmailTemplateEntity> templateEntities = query.getResultList();
		
		if (templateEntities != null && templateEntities.size() > 0) {
			return extractLocales(templateEntities);
		} else {
			return Collections.EMPTY_SET;
		}		
	}
	
	private Set<String> extractLocales(List<EmailTemplateEntity> templateEntities) {
		Set<String> localeSet = new HashSet<String>();
		for (EmailTemplateEntity et : templateEntities) {
			localeSet.add(et.getLocale());
		}
		
		return localeSet;
	}	
	
	/**
	 * Retrieves templates matches the criteria. Returns empty list if no results found.
	 * 
	 * @param eventName
	 * @param localeName
	 * @return List<Template>
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Template> getTemplates(Template template) {
		logger.debug("Entered [getTemplates] method of [EmailTemplateDAOImpl]");
		
		StringBuilder sb = new StringBuilder();
		sb.append("select e from EmailTemplateEntity e ");
		
		if (template.isActive()) {
			sb.append("where ");
			sb.append("e.active = 1 ");
		} else {
			sb.append("where ");
			sb.append("e.active = 0 ");			
		}
		
		if (template.getEvent() != null && template.getEvent().getEventName() != null) {
			sb.append("and e.emailEvent.name = " + "\'" + template.getEvent().getEventName() + "\'" + " ");
		}
		
		if (template.isDefaultTemplate()) {
			sb.append("and e.defaultTemplate = 1 ");
		}
		
		if (template.getName() != null) {
			sb.append("and e.name = " + "\'" + template.getName() + "\'"  + " ");
		}
		
		if (template.getLocale() != null) {
			sb.append("and e.locale = " + "\'"  + template.getLocale() + "\'" );
		}
		
		Query query = entityManager.createQuery(sb.toString().trim());
		List<EmailTemplateEntity> templateEntities = query.getResultList();
		
		if (templateEntities != null && templateEntities.size() > 0) {
			List<Template> emailTemplates = new ArrayList<Template>();
			
			for (Template emailTemplate : BeanTransformer.transform(templateEntities)) {
				emailTemplates.add(populateTemplateImages(emailTemplate));
			}
			
			return emailTemplates;
		} else {
			return Collections.EMPTY_LIST;
		}		
	}
	
	/**
	 * Adds an image or attachment to template
	 * 
	 * @param templateId
	 * @param imageName
	 * @param content
	 * @param mimeType
	 * @param isAttachment
	 * @throws TemplateNotFoundException
	 */
	@Override
	public void addTemplateContent(int templateId, String imageName, byte[] content, String mimeType, boolean isAttachment) throws TemplateNotFoundException {
		logger.debug("Entered [addImage] of [EmailTemplateDAOImpl]");
		
		// Check if template already exists or not
		EmailTemplateEntity et = entityManager.find(EmailTemplateEntity.class, templateId);
		
		if (et == null) {
			logger.info("Email Template for the template Id " + templateId + " not found in the system");
			throw new TemplateNotFoundException("Email Template for the template Id " + templateId + " not found in the system");
		}
		
		EmailTemplateContentEntity eti = new EmailTemplateContentEntity();
		eti.setName(imageName);
		eti.setContent(content);
		eti.setEmailTemplate(et);
		eti.setMimeType(mimeType);
		eti.setAttachment(isAttachment ? 1 : 0);
		
		entityManager.persist(eti);
	}	

}
